// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FBL_STRING_PIECE_H_
#define FBL_STRING_PIECE_H_

#include <string.h>
#include <type_traits>

#include <fbl/string_traits.h>

namespace fbl {

constexpr static size_t constexpr_strlen(const char* str) {
#if defined(_MSC_VER)
#error "__builtin_strlen not defined for MSVC++"
#else
  return __builtin_strlen(str);
#endif
}

// A string-like object that points to a sized piece of memory.
//
// fbl::StringPiece is designed to resemble std::string_view.
//
// |length()| does NOT include a trailing NUL and no guarantee is made that
// you can check |data()[length()]| to see if a NUL is there.
//
// Basically, these aren't C strings, don't think otherwise.
// The string piece does not own the data it points to.
class StringPiece {
 public:
  constexpr StringPiece() : data_(nullptr), length_(0U) {}

  constexpr StringPiece(const char* data)
      : data_(data), length_(data != nullptr ? constexpr_strlen(data) : 0U) {}

  constexpr StringPiece(const char* data, size_t length) : data_(data), length_(length) {}

  constexpr StringPiece(const StringPiece& other) = default;
  constexpr StringPiece(StringPiece&& other) = default;

  // Creates a string piece from a string-like object.
  //
  // Works with various string types including fbl::String, fbl::StringView,
  // std::string, and std::string_view.
  template <typename T, typename = typename std::enable_if<is_string_like<T>::value>::type>
  constexpr StringPiece(const T& value)
      : StringPiece(GetStringData(value), GetStringLength(value)) {}

  constexpr const char* data() const { return data_; }

  constexpr size_t length() const { return length_; }
  constexpr size_t size() const { return length_; }
  constexpr bool empty() const { return length_ == 0U; }

  constexpr const char* begin() const { return data_; }
  constexpr const char* cbegin() const { return data_; }
  constexpr const char* end() const { return data_ + length_; }
  constexpr const char* cend() const { return data_ + length_; }

  constexpr const char& operator[](size_t pos) const { return data_[pos]; }

  int compare(const StringPiece& other) const;

  void clear() {
    data_ = nullptr;
    length_ = 0U;
  }

  constexpr StringPiece& operator=(const StringPiece& other) = default;
  constexpr StringPiece& operator=(StringPiece&& other) = default;

  template <typename T, typename = typename std::enable_if<is_string_like<T>::value>::type>
  constexpr StringPiece& operator=(const T& value) {
    set(GetStringData(value), GetStringLength(value));
    return *this;
  }

  void set(const char* data) {
    data_ = data;
    length_ = data != nullptr ? constexpr_strlen(data) : 0U;
  }

  void set(const char* data, size_t length) {
    data_ = data;
    length_ = length;
  }

 private:
  // Pointer to string data, not necessarily null terminated.
  const char* data_;

  // Length of the string data.
  size_t length_;
};

bool operator==(const StringPiece& lhs, const StringPiece& rhs);

inline bool operator!=(const StringPiece& lhs, const StringPiece& rhs) { return !(lhs == rhs); }

inline bool operator<(const StringPiece& lhs, const StringPiece& rhs) {
  return lhs.compare(rhs) < 0;
}

inline bool operator>(const StringPiece& lhs, const StringPiece& rhs) {
  return lhs.compare(rhs) > 0;
}

inline bool operator<=(const StringPiece& lhs, const StringPiece& rhs) {
  return lhs.compare(rhs) <= 0;
}

inline bool operator>=(const StringPiece& lhs, const StringPiece& rhs) {
  return lhs.compare(rhs) >= 0;
}

}  // namespace fbl

#endif  // FBL_STRING_PIECE_H_
